﻿#define _CRT_SECURE_NO_WARNINGS 1


#include <stdio.h>
//结构体关键字 struct 
// 结构体变量的创建和初始化
//struct S //S是结构体标签
//{
//	char c;
//	int i;
//	double d;
//};
//
//int main()
//{
//	struct S s;
//
//	return 0;
//}
//
//struct S
//{
//	char c;
//	int i;
//	double d;
//}s1;//可以把结构体变量直接放在这
//
//int main()
//{
//	struct S s2;//也可以在其他地方创建结构体变量
//
//	return 0;
//}


//在声明结构的时候，可以不完全的声明。
// 上⾯的两个结构在声明的时候省略掉了结构体标签（tag）。
//struct 
//{
//	char c;
//	int i;
//	double d;
//}s1, s2;


//struct
//{
//	char c;
//	int i;
//	double d;
//}s1;
//
//struct
//{
//	char c;
//	int i;
//	double d;
//}* ps;
//
//int main()
//{
//	ps = &s1;//?
//	return 0;
//}
/***************************************************************
警告：
编译器会把上⾯的两个声明当成完全不同的两个类型，所以是⾮法的。
匿名的结构体类型，如果没有对结构体类型重命名的话，基本上只能使⽤⼀次。
**************************************************************/
//正确的⾃引⽤⽅式：
//struct Node
//{
//	int data;//存放数据 -- 数据域
//	struct Node* next;//-- 指针域  加上指针
//};

//typedef 自定义类型 相当于给类型再重新起个名字
//typedef struct Node
//{
//	int data;//存放数据 -- 数据域
//	struct Node* next;//-- 指针域
//}Node;
//
//
//int main()
//{
//
//	return 0;
//}


//偏移量计算
//#include <stddef.h>
//
//struct S1
//{
//	char c1;//偏移量0   结构体的第⼀个成员对⻬都是对齐到结构体变量起始位置偏移量为0的地址处
//	char c2;//1
//	int n;//4
//};
//
//struct S2
//{
//	char c1;//偏移量0
//	int n;//4
//	char c2;//8
//};
//
// offsetof 函数
// 计算结构体成员相较于结构体变量起始位置的偏移量
// 包含头文件#include <stddef.h>
//int main()
//{
//	//printf("%zd\n", sizeof(struct S1));
//	//printf("%zd\n", sizeof(struct S2));
//	//struct S1 s1 = {0};
//	//printf("%zd\n", offsetof(struct S1, c1));
//	//printf("%zd\n", offsetof(struct S1, c2));
//	//printf("%zd\n", offsetof(struct S1, n));
//	
//	printf("%zd\n", offsetof(struct S2, c1));
//	printf("%zd\n", offsetof(struct S2, n));
//	printf("%zd\n", offsetof(struct S2, c2));
//
//	return 0;
//}


/***************************************************************************************************
* 结构体内存对⻬
* 对⻬规则
* 1. 结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处
  2. 其他成员变量要对⻬到某个数字（对⻬数）的整数倍的地址处。
     对⻬数 = 编译器默认的⼀个对⻬数 与 该成员变量⼤⼩的较⼩值。
- VS 中默认的值为 8
- Linux中 gcc 没有默认对⻬数，对⻬数就是成员⾃⾝的⼤⼩
3. 结构体总⼤⼩为最⼤对⻬数（结构体中每个成员变量都有⼀个对⻬数，所有对⻬数中最⼤的）的整数倍。
4. 如果嵌套了结构体的情况，嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处，结构
体的整体⼤⼩就是所有最⼤对⻬数（含嵌套结构体中成员的对⻬数）的整数倍。
结构体的内存对⻬是拿空间来换取时间的做法。
**************************************************************************************************/
//struct S1//结构体总大小8
//{
//	char c1;
//	char c2;
//	int n;
//};
//
//struct S2//结构体总大小12
//{
//	char c1;
//	int n;
//	char c2;
//};
//
//struct S3//结构体总大小16
//{
//	double d;
//	char c;
//	int i;
//};
//
//struct S4//结构体总大小32
//{
//	char c1;
//	struct S3 s3;//嵌套结构体
//	double d;
//};
//
//int main()
//{
//	struct S4 s4;
//	printf("%zd\n", sizeof(struct S4));//sizeof计算内存大小
//
//	return 0;
//}


/***************************************************************************************************
#pragma pack()
pragma 这个预处理指令，可以改变编译器的默认对⻬数。
可通过这函数来修改其默认对齐数 
结构体在对⻬⽅式不合适的时候，我们可以⾃⼰更改默认对⻬数
***************************************************************************************************/
//VS上默认对齐数是8
//
//#pragma pack(2)//设置默认对⻬数为2
//
//struct S
//{
//	char c1;//1 1 1
//	int n;  //4 1 1
//	char c2;//1 1 1
//};
//
//#pragma pack()//取消设置的对⻬数，还原为默认
//
//int main()
//{
//	printf("%zd\n", sizeof(struct S));
//
//	return 0;
//}
//
/*********************************************
结构体传参
结构体传参的时候，要传结构体的地址。
**********************************************/
//struct S
//{
//	int arr[1000];
//	int n;
//	char ch;
//};
//
//
//void print1(struct S tmp)//传值调用
//{
//	int i = 0;
//	for (i = 0; i < 10; i++)
//	{
//		printf("%d ", tmp.arr[i]);
//	}
//	printf("\n");
//	printf("n = %d\n", tmp.n);
//	printf("ch = %c\n", tmp.ch);
//}
//
//void print2(const struct S* ps)//传址调用
//{
//	int i = 0;
//	for (i = 0; i < 10; i++)
//	{
//		printf("%d ", ps->arr[i]);
//	}
//	printf("\n");
//	printf("n = %d\n", ps->n);
//	printf("ch = %c\n", ps->ch);
//
//}
//
//int main()
//{
//	struct S s = { {1,2,3,4,5,6,7,8,9,10},  10, 'w'};
//	print1(s);
//	print2(&s);
//
//	return 0;
//}

/*********************************************
位段
位段的声明和结构是类似的，有两个不同：
1. 位段的成员必须是 int、unsigned int 或signed int ，在C99中位段成员的类型也可以选择其他类型。
2. 位段的成员名后边有⼀个冒号和⼀个数字。
**********************************************/

//struct S
//{
//	int _a;
//	int _b;
//	int _c;
//	int _d;
//};
//
//int main()
//{
//	printf("%zd\n", sizeof(struct S));
//
//	return 0;
//}
/*************************************************************
位段的内存分配
1. 位段的成员可以是 int unsigned int signed int 或者是 char 等类型
2. 位段的空间上是按照需要以4个字节（ int ）或者1个字节（ char ）的⽅式来开辟的。
3. 位段涉及很多不确定因素，位段是不跨平台的，注重可移植的程序应该避免使⽤位段。
跟结构相⽐，位段可以达到同样的效果，并且可以很好的节省空间，但是有跨平台的问题存在。
*************************************************************/
//struct S
//{
//    char a : 3;//3个比特位空间
//    char b : 4;
//    char c : 5;
//    char d : 4;
//};
//
//int main()
//{
//    struct S s = { 0 };
//    s.a = 10;
//    s.b = 12;
//    s.c = 3;
//    s.d = 4;
//
//
//    printf("%zd\n", sizeof(struct S));
//    return 0;
//}


//struct A
//{
//    int _a : 2;
//    int _b : 5;
//    int _c : 10;
//    int _d : 30;
//};
//
//
//int main()
//{
//    struct A sa = {0};
//    int d = 0;
//    scanf("%d", &d);
//    sa._d = d;
//    printf("%d\n", sa._d);
//
//    return 0;
//}
// 
// 
 /*****************************
 enum 枚举
枚举顾名思义就是⼀⼀列举。
把可能的取值⼀⼀列举。
 ******************************/
//#define MALE 3 //#define 用来定义常量的 
//#define FEMALE 5
//#define SECRET 7

//
//enum Sex
//{
//	//该枚举类型的三种可能取值
//	//他们都是常量，被称为枚举常量
//	MALE=3,//0
//	FEMALE=5,
//	SECRET=7
//};
//
//
//int main()
//{
//	enum Sex sex1 = MALE;
//	enum Sex sex2 = FEMALE;
//	printf("%d\n", MALE);
//	printf("%d\n", FEMALE);
//	printf("%d\n", SECRET);
//
//	return 0;
//}
//